package chpn.id;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Random;

/**
 * 改进版雪花算法 (毫秒级)
 * 获取64位整数的算法
 * 1bit  [0]     数值固定为 0
 * 42bit [ 1-42] 当前时间戳 减去 2025-01-01
 * 4bit  [43-46 ] worker序号(16个号)
 * 17bit [47-63]  循环自增序号 ，初始为随机数（131072个id）
 * 每次获取id 检查是否发生时间回拨  ， 如果当前时间小于上次的时间 ， 则更新worker序号
 */
public class SnowflakeMsJ {
    // worker ID (0-15)
    long workerId;
    
    // 起始时间戳 - 2025-01-01 00:00:00
    private final long START_TIMESTAMP = LocalDateTime.of(2025, 1, 1, 0, 0, 0)
            .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;
    
    // 序列号，初始为随机数
    private long sequence;
    
    // 同一毫秒内第一个序列号
    private long firstSequenceInMillis;
    
    // 各部分占用的位数
    private final long TIMESTAMP_BITS = 42L;
    private final long WORKER_ID_BITS = 4L;
    private final long SEQUENCE_BITS = 17L;
    
    // 各部分的最大值
    private final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); // 15
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); // 131071
    
    // 各部分向左的位移
    private final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private final long TIMESTAMP_SHIFT = WORKER_ID_BITS + SEQUENCE_BITS;
    WorkerIdGenerator workerIdGenerator ;
    
    public SnowflakeMsJ(WorkerIdGenerator workerIdGenerator) {
        this.workerIdGenerator = workerIdGenerator;
        // 初始化序列号为随机数
        this.sequence = new Random().nextInt((int) MAX_SEQUENCE);
    }
    

    public long getWorkerId() {
        return workerId;
    }
    /**
     * 获取一个新的ID
     */
    public synchronized long getNextId() {
        long currentTimestamp = getCurrentTimestamp();
        
        // 处理时间回拨问题
        if (currentTimestamp < lastTimestamp) {
            // 如果发生时间回拨，更新机器序列号
            workerId = workerIdGenerator.getNewWorkerId() ;
            // 重置序列号
            sequence = new Random().nextInt((int) MAX_SEQUENCE);
        }
        
        // 如果是同一毫秒内，则增加序列号
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;

            // 如果序列号回到了同一毫秒内第一个序列号，表示已经循环一圈，抛出异常
            if (sequence == firstSequenceInMillis) {
                throw new RuntimeException("序列号溢出");
            }
        } else {
            // 不同毫秒内，重置序列号为随机数
            sequence = new Random().nextInt((int) MAX_SEQUENCE);
            // 记录同一毫秒内第一个序列号
            firstSequenceInMillis = sequence;
        }
        
        // 更新上次生成ID的时间戳
        lastTimestamp = currentTimestamp;
        
        // 组装ID
        return (0L) | // 第0位固定为1
               ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) | // 时间戳部分
               (workerId << WORKER_ID_SHIFT) | // worker ID部分
               sequence; // 序列号部分
    }
    
    /**
     * 获取当前时间戳
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }
    
    /**
     * 等待下一毫秒
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }
}
